#include "buffer.h"
#include <stdio.h>
#include <fcntl.h>
/*
 * This code handles all hd-interrupts, and read/write requests to
 * the hard-disk. It is relatively straigthforward (not obvious maybe,
 * but interrupts never are), while still being efficient, and never
 * disabling interrupts (except to overcome possible race-condition).
 * The elevator block-seek algorithm doesn't need to disable interrupts
 * due to clever programming.
 */

/* Max read/write errors/sector */
#define MAX_ERRORS	5
#define MAX_HD		2
#define NR_REQUEST	32

#define HD_TYPE { 5,17,980,300,980,0 }
extern struct super_block super_block[];
extern struct task_struct * current;
/*
 *  This struct defines the HD's and their types.
 *  Currently defined for CP3044's, ie a modified
 *  type 17.
 */
static struct hd_i_struct{
	int head,sect,cyl,wpcom,lzone,ctl;
	} hd_info[]= { HD_TYPE };

#define NR_HD ((sizeof (hd_info))/(sizeof (struct hd_i_struct)))
int lHD[NR_HD];

struct hd_struct {
	long start_sect;
	long nr_sects;
} hd[5*MAX_HD]={{0,0},};
	
extern struct buffer_head * start_buffer;

static inline void lock_buffer(struct buffer_head * bh)
{
	if (bh->b_lock)
		printk("hd.c: buffer multiply locked\n");
	bh->b_lock=1;
}

static inline void unlock_buffer(struct buffer_head * bh)
{
	if (!bh->b_lock)
		printk("hd.c: free buffer being unlocked\n");
	bh->b_lock=0;
	wake_up(&bh->b_wait);
}

static inline void wait_on_buffer(struct buffer_head * bh)
{
	cli();
	while (bh->b_lock)
		sleep_on(&bh->b_wait);
	sti();
}

void rw_hd(int rw, struct buffer_head * bh)
{
	unsigned int block,dev;

	block = bh->b_blocknr << 1;
	dev = MINOR(bh->b_dev);
	if (dev >= 5*NR_HD || block+2 > hd[dev].nr_sects)
		return;
	block += hd[dev].start_sect;
	dev /= 5;
	rw_logical_hd(rw, dev, block+1, bh);
//	sec = block % hd_info[dev].sector;
//	cyl = block / hd_info[dev].sector / hd_info[dev].head;
//	head= block / hd_info[dev].sector % hd_info[dev].head;
}
void dump_partition(struct partition *p)
{
      printk("boot_ind 0x%x, head 0x%x.\n\r",(p-4)->boot_ind, (p-4)->head);
      printk("sector 0x%x, cyl 0x%x.\n\r",(p-4)->sector, (p-4)->cyl);
      printk("sys_ind 0x%x, end_head 0x%x.\n\r",(p-4)->sys_ind, (p-4)->end_head);
      printk("end_sector 0x%x, end_cyl 0x%x.\n\r",(p-4)->end_sector, (p-4)->end_cyl);
      printk("Start sec 0x%x, nr sects 0x%x.\n\r",(p-4)->start_sect ,(p-4)->nr_sects);
}
/* This may be used only once, enforced by 'static int callable' */
int sys_setup(void)
{
	static int callable = 1;
	int i,drive;
	struct partition *p;

	if (!callable)
		return -1;
	callable = 0;
	for (drive=0 ; drive<NR_HD ; drive++) {
		rw_abs_hd(READ,drive,1,0,0,(struct buffer_head *) start_buffer);
		if (!start_buffer->b_uptodate) {
			printk("Unable to read partition table of drive %d\n\r",
				drive);
			panic("");
		}
		if (start_buffer->b_data[510] != 0x55 || (unsigned char)
		    start_buffer->b_data[511] != 0xAA) {
			printk("Bad partition table on drive %d\n\r",drive);
			panic("");
		}
		p = 0x1BE + (void *)start_buffer->b_data;
		for (i=1;i<5;i++,p++) {
			hd[i+5*drive].start_sect = p->start_sect;
			hd[i+5*drive].nr_sects = p->nr_sects;
		}
	}
	printk("Partition table%s ok.\n\r",(NR_HD>1)?"s":"");
	dump_partition(p);
	mount_root();
	return (0);
}

void rw_abs_hd(int rw,unsigned int nr,unsigned long sec,unsigned int head,
	unsigned int cyl,struct buffer_head * bh)
{
	unsigned long lsec;
	lsec = sec + cyl * hd_info[nr].sect * head; 
	rw_logical_hd(rw, nr, lsec, bh);
	bh->b_uptodate = 1;
}

void rw_logical_hd(int rw, int nr, unsigned long sec, struct buffer_head * bh)
{
        int c = 0;
	int dev = lHD[nr];
        
        printk("R/W sector 0x%x.\n\r", sec);

        if(lseek(dev,(sec-1)*512,SEEK_SET) >= 0)
        {
                if(rw == READ)
                        c = read(dev, bh->b_data, BLOCK_SIZE);
                else
                        c = write(dev, bh->b_data, BLOCK_SIZE);
        }
        else
        {
                panic("Read/Write Sector failed!\n");
        }
}

void hd_init(char **argv)
{
	int i,id;

	for (i=0 ; i<NR_HD ; i++) {
		hd[i*5].start_sect = 0;
		hd[i*5].nr_sects = hd_info[i].head*
				hd_info[i].sect*hd_info[i].cyl;
	
		if ((id=open(argv[i+2],O_RDWR,0))<0)
			panic("Unable to open 'diskimg'");

		lHD[i] = id;
	}
}

void sys_install()
{
        int i,drive;
        struct partition *p;

        for (drive=0 ; drive<NR_HD ; drive++) {
                rw_abs_hd(READ,drive,1,0,0,(struct buffer_head *) start_buffer);
                if (!start_buffer->b_uptodate) {
                        printk("Unable to read partition table of drive %d\n\r",
                                drive);
                        panic("");
                }
                if (start_buffer->b_data[510] != 0x55 || (unsigned char)
                    start_buffer->b_data[511] != 0xAA) {
                        printk("Bad partition table on drive %d\n\r",drive);
                        panic("");
                }
                p = 0x1BE + (void *)start_buffer->b_data;
                for (i=1;i<5;i++,p++) {
                        hd[i+5*drive].start_sect = p->start_sect;
                        hd[i+5*drive].nr_sects = p->nr_sects;
                }
        }
        printk("Partition table%s ok.\n\r",(NR_HD>1)?"s":"");
        dump_partition(p);

	put_super();
}

void put_super()
{
	struct super_block * p;
	struct buffer_head * bh;
	int drive = MINOR(ROOT_DEV) / 5;
	int block;
	struct m_inode * inode;
	struct dir_entry * de;
	int mode = 0;

	if (!(bh = bread(ROOT_DEV,1)))
		return;
        p = (struct super_block *) (bh->b_data);
        p->s_ninodes = 8191;
        p->s_nzones = hd[drive].nr_sects / 2;
	p->s_imap_blocks = p->s_ninodes / 8192 + 1;
	p->s_zmap_blocks = p->s_nzones / 8192 + 1;
	p->s_firstdatazone = 1 + 1
		+ p->s_imap_blocks + p->s_zmap_blocks
		+ p->s_ninodes / INODES_PER_BLOCK + 1;
	p->s_log_zone_size = 0;
	p->s_max_size = 0x10801c00;
	p->s_magic = 0x137f;
        bh->b_dirt = 1;

	block = 1;
	while((++block) <= p->s_firstdatazone){
		if (!(bh = bread(ROOT_DEV,block)))
			panic("put_super: blk read error");
		memset(bh->b_data,0,BLOCK_SIZE);
		bh->b_dirt = 1;
		brelse(bh);
	}
	sys_sync();

	do_mount(ROOT_DEV);

        inode = new_inode(ROOT_DEV);
        if (!inode) {
                panic("put_super: new inode fail.");
        }
        inode->i_size = 32;
        inode->i_dirt = 1;
        inode->i_mtime = inode->i_atime = CURRENT_TIME;
        
	if (!(inode->i_zone[0]=new_block(inode->i_dev))) 
                panic("put_super: new block for ROOT_DIR fail.");
	if (!(bh=bread(inode->i_dev,inode->i_zone[0]))) 
		panic("put_super: read block for ROOT_DIR fail.");
        
        de = (struct dir_entry *) bh->b_data;
        de->inode=inode->i_num;
        strcpy(de->name,".");
        de++;
        de->inode = inode->i_num;
        strcpy(de->name,"..");
        inode->i_nlinks = 2;
        bh->b_dirt = 1;
        brelse(bh);
        inode->i_mode = I_DIRECTORY | (mode & 0777 & ~current->umask);
        iput(inode);
	
        sys_sync();
}

void sys_ls(char *pathname)
{
        struct m_inode * inode;

        sys_setup();

        if (!(inode = get_dir(pathname)))
                panic("sys_ls: cannot find pathname.");
}


void sys_adddir()
{
	sys_setup();
	sys_mkdir("dev", 0);

	sys_sync();
}

void sys_writefile(char * pathname)
{
	int id, c, did;
	char buffer[BLOCK_SIZE];

        if ((id=open(pathname,O_RDWR,0))<0)
		panic("sys_writefile: Unable to open file");
	sys_setup();
	did = sys_creat(pathname, S_IFBLK);
	while(c = read(id, buffer, BLOCK_SIZE)){
		if(c==EOF)
			return;
		else
			sys_write(did, buffer, c);
	}
	sys_sync();
}

void sys_readfile(char * pathname)
{
        int id, c;
        char buffer[BLOCK_SIZE];

        sys_setup();
	if(id = sys_open(pathname, O_RDWR, 0) < 0)
		panic("sys_readfile: Unable to open file");
        while(c = sys_read(id, buffer, BLOCK_SIZE)){
                if(c==EOF)
                        return;
                else
                        printk("%s", buffer);
        }
}

